package io.github.chutian0610.sqlparser.token

import arrow.core.toOption
import java.lang.Character.isLetter

data class CodePoint(val value: Int) {

    private fun matchesChar(c: Char): Boolean {
        return value == c.code
    }

    fun inIntRange(range: IntRange): Boolean {
        return value in range
    }

    fun toUnicodeChar(): String {
        return if (this.value <= Char.MAX_VALUE.code) {
            Char(this.value).toString()
        } else {
            String(Character.toChars(this.value))
        }
    }

    fun toDigit(radix: Int = 10): Int? {
        return try {
            val c = Character.digit(this.value, radix)
            if (c == -1) null else c
        } catch (e: IllegalArgumentException) {
            null // 无效字符返回 null
        }
    }

    fun isDigit(radix: Int = 10): Boolean {
        return toDigit(radix).toOption().isSome()
    }

    fun isAsciiDigit(): Boolean {
        // code in ASCII `0`~`9`
        return this.value in 48..57
    }

    fun isAlphabetic(): Boolean {
        return isLetter(this.value)
    }

    fun isAscii(): Boolean {
        return this.value in 0..127
    }

    fun isAsciiUppercase(): Boolean {
        return this.value in 65..90  // 'A'=65, 'Z'=90
    }

    fun isAsciiLowercase(): Boolean {
        return this.value in 97..122  // 'a'=97, 'z'=122
    }

    fun isAlphanumeric(): Boolean {
        return this.isAlphabetic() || this.isNumeric()
    }

    fun isAsciiHexDigit(): Boolean {
        return this.value in 48..57 ||  // code in ASCII `0`~`9`
                this.value in 65..70 || // code in ASCII `A`~`F`
                this.value in 97..102  // code in ASCII `a`~`f`
    }

    fun isWhitespace(): Boolean {
        return Character.isWhitespace(this.value) || Character.isSpaceChar(this.value)
    }

    fun isNumeric(): Boolean {
        val category = CharCategory.valueOf(Character.getType(this.value))
        return when (category) {
            CharCategory.DECIMAL_DIGIT_NUMBER,  // Nd
            CharCategory.LETTER_NUMBER,         // Nl
            CharCategory.OTHER_NUMBER           // No
                -> true

            else -> false
        }
    }


    companion object {
        fun fromChar(c: Char): CodePoint {
            return CodePoint(c.code)
        }

        fun fromInt(i: Int): CodePoint {
            return CodePoint(i)
        }

        fun equalsChar(c: CodePoint?, char: Char): Boolean {
            return c?.matchesChar(char) ?: false
        }

        fun fromString(s: String): CodePoint {
            return CodePoint(s.codePointAt(0))
        }

        fun inCharArray(char: CodePoint?, vararg chars: Char): Boolean {
            return char?.let { it.value in chars.map { c -> c.code } } ?: false
        }

        fun inCharRange(char: CodePoint?, chars: CharRange): Boolean {
            return char?.let { it.value in chars.map { it.code } } ?: false
        }

        fun inIntRange(char: CodePoint?, ints: IntRange): Boolean {
            return char?.let { it.value in ints } ?: false
        }
    }
}
